package net.aiion.weave.impl.web.service.http;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import net.aiion.weave.impl.web.servlet.http.WeaveServletSessionListener;
import net.aiion.weave.spi.web.service.WeaveServiceException;
import net.aiion.weave.spi.web.service.WeaveServiceRequest;
import net.aiion.weave.util.net.Decoder;
import net.aiion.weave.util.net.URLDecoder;

public final class WeaveServiceRequestImpl implements WeaveServiceRequest
{
    private static final String SESSION_CONTEXT_ID_PARAMETER = WeaveServletSessionListener.SESSION_CONTEXT_ID_PARAMETER;
    private static final String CONVERSATION_CONTEXT_ID_PARAMETER = "cid";

    private final HttpServletRequest httpServletRequest;

    private ByteArrayOutputStream byteArrayOutputStream;
    private Map<String, List<String>> parameterValues;

    public WeaveServiceRequestImpl(final HttpServletRequest aHttpServletRequest) {
        httpServletRequest = aHttpServletRequest;

        parameterValues = new HashMap<String, List<String>>();
        parseRequest();
    }

    private void parseRequest() {
        switch (httpServletRequest.getMethod()) {
        case "GET":
            parseGetRequest();
            break;
        case "POST":
            parsePostRequest();
            break;
        default:
            throw new WeaveServiceException("");
        }
    }

    private void parseGetRequest() {
        for (final Enumeration<String> parameterNames = httpServletRequest.getParameterNames(); parameterNames
            .hasMoreElements();)
        {
            final String parameterName = parameterNames.nextElement();
            for (String parameterValue : httpServletRequest.getParameterValues(parameterName)) {
                addParameter(parameterName, parameterValue);
            }
        }
    }

    private void parsePostRequest() {
        try {
            final InputStream is = httpServletRequest.getInputStream();
            byteArrayOutputStream = new ByteArrayOutputStream();
            final StringBuilder paramName = new StringBuilder();
            final StringBuilder paramValue = new StringBuilder();
            final Decoder urlDecoder = URLDecoder.getInstance();
            int state = 0;

            int b;
            while ((b = is.read()) != -1) {
                switch (state) {
                case 0:
                    if (b == '=') {
                        state = 1;
                        continue;
                    }

                    byteArrayOutputStream.write(b);
                    paramName.append((char) b);
                    break;
                case 1:
                    if (b == '&') {
                        addParameter(urlDecoder.decode(paramName.toString()), urlDecoder.decode(paramValue.toString()));
                        paramName.setLength(0);
                        paramValue.setLength(0);
                        state = 0;
                        continue;
                    }

                    byteArrayOutputStream.write(b);
                    paramValue.append((char) b);
                    break;
                default:
                    break;
                }
            }

            if (paramValue.length() > 0) {
                addParameter(urlDecoder.decode(paramName.toString()), urlDecoder.decode(paramValue.toString()));
            }
        } catch (final IOException e) {
            throw new WeaveServiceException(e);
        }
    }

    private void addParameter(final String aParameterName, final String aParameterValue) {
        if (!parameterValues.containsKey(aParameterName)) {
            parameterValues.put(aParameterName, new ArrayList<String>());
        }

        parameterValues.get(aParameterName).add(aParameterValue);
    }

    @Override
    public Collection<String> getParameterNames() {
        return parameterValues.keySet();
    }

    @Override
    public Collection<String> getParameterValues(final String aParameterName) {
        return parameterValues.get(aParameterName);
    }

    @Override
    public String getSessionContextId() {
        return String.valueOf(httpServletRequest.getSession(true).getAttribute(SESSION_CONTEXT_ID_PARAMETER));
    }

    @Override
    public String getConversationContextId() {
        if (parameterValues.containsKey(CONVERSATION_CONTEXT_ID_PARAMETER)) {
            return parameterValues.get(CONVERSATION_CONTEXT_ID_PARAMETER).get(0);
        }

        return null;
    }

    @Override
    public String getRequestURI(final String aConversationContextId) {
        return buildURI(httpServletRequest.getRequestURL(), aConversationContextId);
    }

    @Override
    public String getLocalURI(final String aConversationContextId) {
        return httpServletRequest.getPathInfo();
    }

    private String buildURI(final CharSequence aBaseURI, final String aConversationContextId) {
        final StringBuilder uriBuilder = new StringBuilder(aBaseURI);
        uriBuilder.append('?');
        uriBuilder.append(CONVERSATION_CONTEXT_ID_PARAMETER);
        uriBuilder.append('=');
        uriBuilder.append(aConversationContextId);
        return uriBuilder.toString();
    }
}
